////////////////////////////////////////////////////////////
// Project: T-Junction Traffic Light
// Ed Bulpitt - 2317250
////////////////////////////////////////////////////////////

#include <16F877A.h> // Include header for PIC16F877A microcontroller

// === CONFIG ===
#fuses XT, NOWDT, NOPROTECT, NOLVP // Set microcontroller configuration: use XT crystal, no watchdog timer, no code protection, no low-voltage programming
#use delay(clock=4000000)          // Set clock frequency to 4 MHz

// === PIN ASSIGNMENTS ===
// Main road West direction traffic lights
#define MW_RED    PIN_A0
#define MW_YEL    PIN_A1
#define MW_GRN    PIN_A2
// Main road East direction traffic lights
#define ME_RED    PIN_A3
#define ME_YEL    PIN_B3
#define ME_GRN    PIN_A5
// Narrow Side road (South direction)
#define NS_RED    PIN_B1
#define NS_YEL    PIN_B2
#define NS_GRN    PIN_B4
// Pedestrian lights and button
#define PD_RED    PIN_B7
#define PD_YEL    PIN_B6
#define PD_GRN    PIN_B0
#define BUTTON    PIN_B5   // Pedestrian call button (active-low = pressed when pin reads 0)

// === 7-SEGMENT MAP ===
// Binary representation of digits 0–9 for a 7-segment display
const unsigned char SEG_MAP[10] = {
   0b0111111,0b0000110,0b1011011,0b1001111,
   0b1100110,0b1101101,0b1111101,0b0000111,
   0b1111111,0b1101111
};
// Segment pin mappings for tens and unit digits
const int8 TENS_PINS[7] = {PIN_D0,PIN_D1,PIN_D2,PIN_D3,PIN_D4,PIN_D5,PIN_D6};
const int8 UNIT_PINS[7]= {PIN_C1,PIN_C2,PIN_C3,PIN_C4,PIN_C5,PIN_C6,PIN_C7};

// === TIMING ===
#define VEH_BLOCKS         4    // Number of 10s blocks in the vehicle cycle (total 40s)
#define BLOCK_DURATION    10    // Each block is 10 seconds
#define GREEN_TIME         7    // Green lasts 7 seconds per block
#define YELLOW_TIME        3    // Yellow lasts 3 seconds per block
#define SAFETY_RED_TIME    3    // Safety buffer with all red lights for 3 seconds
#define PED_TIME          10    // Pedestrian crossing time is 10 seconds

// Choose one display mode
#define DISPLAY_COUNTUP     // Displays timer counting up (remove to count down)

// === Light state definitions ===
#define LS_STOP   0    // Red light
#define LS_GO     1    // Green light
#define LS_SLOW   2    // Yellow light


// === PROTOTYPES ===
void init_io(void);                   // Function to initialize inputs/outputs
void set_light(int r,int y,int g,int st); // Set one traffic light's state
void delay_s(unsigned int s);        // Delay in seconds
void show_2digit(unsigned char v);   // Show 2-digit number on 7-segment display
void service_ped(void);              // Handle pedestrian crossing


// === MAIN FUNCTION ===
void main() {
   int timer;          // Main global timer in seconds
   int block;          // Current 10-second block (0 to 3)
   int sec_in_blk;     // Second within the current block (0 to 9)
   int state;          // Current light state: GO, SLOW, STOP
   int ped_called;     // Flag to check if pedestrian button was pressed

   init_io();          // Set up all input and output pins

   while(TRUE) {       // Infinite loop to keep system running
      timer = 0;
      ped_called = 0;

      // VEHICLE CYCLE with possible interruption from pedestrian
      while(timer < VEH_BLOCKS * BLOCK_DURATION) {
         block = timer / BLOCK_DURATION;       // Determine which 10s block we're in
         sec_in_blk = timer % BLOCK_DURATION;  // Second within that block
         state = (sec_in_blk < GREEN_TIME)? LS_GO : LS_SLOW; // Green for first 7s, then yellow for 3s

         // Alternate main and side roads
         if((block & 1) == 0) {
            // Even block: Main road gets green/yellow, side gets red
            set_light(MW_RED,MW_YEL,MW_GRN,state);
            set_light(ME_RED,ME_YEL,ME_GRN,state);
            set_light(NS_RED,NS_YEL,NS_GRN,LS_STOP);
         } else {
            // Odd block: Side road gets green/yellow, main road gets red
            set_light(MW_RED,MW_YEL,MW_GRN,LS_STOP);
            set_light(ME_RED,ME_YEL,ME_GRN,LS_STOP);
            set_light(NS_RED,NS_YEL,NS_GRN,state);
         }

         // Pedestrian signal always red during vehicle cycle
         output_low(PD_GRN); 
         output_low(PD_YEL); 
         output_high(PD_RED);

         // Show either countdown or countup on 7-segment display
         #ifdef DISPLAY_COUNTDOWN
            show_2digit(VEH_BLOCKS*BLOCK_DURATION - timer);
         #else
            show_2digit(timer);
         #endif

         // Check if pedestrian button is pressed (with debouncing)
         if(!input(BUTTON)) {
            delay_ms(50); // Small delay to filter false presses
            if(!input(BUTTON)) {
               ped_called = 1; // Confirmed press
               break;          // Interrupt vehicle cycle
            }
         }

         delay_s(1); // Wait 1 second
         timer++;    // Increment global timer
      }

      if(ped_called) {
         service_ped(); // Run pedestrian crossing sequence
         // Resume main loop exactly from where it stopped
      }
   }
}


// === INITIALIZE I/O ===
void init_io(void) {
   set_tris_a(0xC1); // Set input/output status for Port A (0xC1 = bits 6 and 7 input, rest output)
   set_tris_b(0x20); // Port B: RB5 (button) input, others output
   set_tris_c(0x01); // RC0 input, rest output (not used)
   set_tris_d(0x80); // RD7 input, RD0–RD6 output (used for TENS digit)
   set_tris_e(0x00); // All Port E pins as output

   // Turn all lights to red and set pedestrian to red
   set_light(MW_RED,MW_YEL,MW_GRN,LS_STOP);
   set_light(ME_RED,ME_YEL,ME_GRN,LS_STOP);
   set_light(NS_RED,NS_YEL,NS_GRN,LS_STOP);
   output_high(PD_RED); 
   output_low(PD_YEL); 
   output_low(PD_GRN);
}


// === SET A TRAFFIC LIGHT TO A GIVEN STATE ===
void set_light(int r,int y,int g,int st) {
   switch(st) {
     case LS_STOP: // Red on
       output_high(r); output_low(y); output_low(g); break;
     case LS_GO:   // Green on
       output_low(r);  output_low(y); output_high(g); break;
     case LS_SLOW: // Yellow on
       output_low(r);  output_high(y); output_low(g); break;
   }
}


// === PEDESTRIAN CROSSING SEQUENCE ===
void service_ped(void) {
   int t;

   // Step 1: Turn all lights yellow for 3 seconds
   set_light(MW_RED,MW_YEL,MW_GRN,LS_SLOW);
   set_light(ME_RED,ME_YEL,ME_GRN,LS_SLOW);
   set_light(NS_RED,NS_YEL,NS_GRN,LS_SLOW);
   output_low(PD_GRN); output_low(PD_YEL); output_high(PD_RED);
   for(t = YELLOW_TIME; t > 0; t--) {
      show_2digit(t);
      delay_s(1);
   }

   // Step 2: All vehicle lights to red for safety (3 seconds)
   set_light(MW_RED,MW_YEL,MW_GRN,LS_STOP);
   set_light(ME_RED,ME_YEL,ME_GRN,LS_STOP);
   set_light(NS_RED,NS_YEL,NS_GRN,LS_STOP);
   output_low(PD_GRN); output_low(PD_YEL); output_high(PD_RED);
   for(t = SAFETY_RED_TIME; t > 0; t--) {
      show_2digit(t);
      delay_s(1);
   }

   // Step 3: Pedestrian green for 10 seconds
   set_light(MW_RED,MW_YEL,MW_GRN,LS_STOP);
   set_light(ME_RED,ME_YEL,ME_GRN,LS_STOP);
   set_light(NS_RED,NS_YEL,NS_GRN,LS_STOP);
   output_low(PD_RED); output_low(PD_YEL); output_high(PD_GRN);
   for(t = PED_TIME; t > 0; t--) {
      show_2digit(t);
      delay_s(1);
   }

   // Step 4: Turn pedestrian green off and red back on
   output_low(PD_GRN);
   output_high(PD_RED);
}


// === DISPLAY 2-DIGIT NUMBER ON 7-SEGMENT ===
void show_2digit(unsigned char v) {
   unsigned char hi = v/10, lo = v%10;     // Separate into tens and units
   unsigned char mhi = SEG_MAP[hi], mlo = SEG_MAP[lo]; // Convert to segment patterns
   int i;
   for(i = 0; i < 7; i++) {
     output_bit(TENS_PINS[i], (mhi >> i) & 1);  // Set tens digit segments
     output_bit(UNIT_PINS[i],(mlo >> i) & 1);   // Set units digit segments
   }
}


// === SIMPLE DELAY FUNCTION (SECONDS) ===
void delay_s(unsigned int s) {
   while(s--) delay_ms(1000); // Delay for 1000 milliseconds per second
}
